package com.example.codediff.analyzer;

import lombok.extern.slf4j.Slf4j;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.tree.*;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 方法级别的差异分析器
 */
@Slf4j
@Component
public class MethodDiffAnalyzer {

    /**
     * 列出JAR包中的所有文件
     */
    private void listJarContents(String jarPath) {
        try (JarFile jarFile = new JarFile(jarPath)) {
            log.info("列出JAR包内容: {}", jarPath);
            jarFile.stream().forEach(entry -> {
                log.info("  - {}", entry.getName());
            });
        } catch (Exception e) {
            log.error("列出JAR包内容失败: {}", jarPath, e);
        }
    }

    /**
     * 分析两个JAR包中指定类的方法差异
     * @param oldJarPath 旧版本JAR包路径
     * @param newJarPath 新版本JAR包路径
     * @param className 要分析的类名
     * @return 发生变化的方法名和描述符列表
     */
    public Set<String> findModifiedMethods(String oldJarPath, String newJarPath, String className) throws IOException {
        log.debug("开始分析类的方法差异: oldJar={}, newJar={}, class={}", oldJarPath, newJarPath, className);
        
        // 先列出JAR包内容
        listJarContents(oldJarPath);
        listJarContents(newJarPath);

        // 读取新旧类文件
        ClassNode oldClass = readClass(oldJarPath, className);
        ClassNode newClass = readClass(newJarPath, className);

        if (oldClass == null || newClass == null) {
            log.warn("无法读取类文件: {}", className);
            return Collections.emptySet();
        }

        Set<String> modifiedMethods = new HashSet<>();

        // 将方法列表转换为Map
        Map<String, MethodNode> oldMethods = getMethodMap(oldClass.methods);
        Map<String, MethodNode> newMethods = getMethodMap(newClass.methods);

        log.debug("类 {} 的方法数量: 旧版本={}, 新版本={}", 
                 className, oldMethods.size(), newMethods.size());

        // 比较方法
        for (Map.Entry<String, MethodNode> entry : newMethods.entrySet()) {
            String methodKey = entry.getKey();
            MethodNode newMethod = entry.getValue();
            MethodNode oldMethod = oldMethods.get(methodKey);

            if (oldMethod == null) {
                // 新增的方法
                log.debug("发现新增方法: {}.{}", className, methodKey);
                modifiedMethods.add(methodKey);
            } else if (!compareMethodBytecode(oldMethod, newMethod)) {
                // 修改的方法
                log.debug("发现修改方法: {}.{}", className, methodKey);
                modifiedMethods.add(methodKey);
            }
        }

        // 检查删除的方法
        for (String methodKey : oldMethods.keySet()) {
            if (!newMethods.containsKey(methodKey)) {
                // 删除的方法也算作变更
                log.debug("发现删除方法: {}.{}", className, methodKey);
                modifiedMethods.add(methodKey);
            }
        }

        log.info("类 {} 的方法差异分析完成，发现 {} 个变更", className, modifiedMethods.size());
        return modifiedMethods;
    }

    /**
     * 从JAR包中读取指定的类
     */
    public ClassNode readClass(String jarPath, String className) throws IOException {
        try (JarFile jarFile = new JarFile(jarPath)) {
            JarEntry entry = jarFile.getJarEntry(className.replace('.', '/') + ".class");
            if (entry == null) {
                log.warn("找不到类文件: {}", className);
                return null;
            }

            try (InputStream in = jarFile.getInputStream(entry)) {
                ClassReader reader = new ClassReader(in);
                ClassNode classNode = new ClassNode();
                reader.accept(classNode, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
                return classNode;
            }
        }
    }
    
    /**
     * 获取方法的指令节点
     */
    public AbstractInsnNode getFirstInstruction(MethodNode method) {
        return method.instructions != null ? method.instructions.getFirst() : null;
    }

    /**
     * 获取下一个指令节点
     */
    public AbstractInsnNode getNextInstruction(AbstractInsnNode insn) {
        return insn != null ? insn.getNext() : null;
    }

    /**
     * 将方法列表转换为Map以便快速查找
     * 键为"方法名"
     */
    private Map<String, MethodNode> getMethodMap(List<MethodNode> methods) {
        Map<String, MethodNode> methodMap = new HashMap<>();
        for (MethodNode method : methods) {
            methodMap.put(method.name, method);
            log.debug("索引方法: {}", method.name);
        }
        return methodMap;
    }

    /**
     * 比较两个方法的字节码
     * 这里使用一个简单的比较策略：比较方法的字节码指令序列
     */
    private boolean compareMethodBytecode(MethodNode oldMethod, MethodNode newMethod) {
        // 比较基本属性
        if (!Objects.equals(oldMethod.access, newMethod.access)) {
            log.debug("方法访问标志不同: old={}, new={}", oldMethod.access, newMethod.access);
            return false;
        }
        
        if (!Objects.equals(oldMethod.desc, newMethod.desc)) {
            log.debug("方法描述符不同: old={}, new={}", oldMethod.desc, newMethod.desc);
            return false;
        }
        
        if (!Objects.equals(oldMethod.signature, newMethod.signature)) {
            log.debug("方法签名不同: old={}, new={}", oldMethod.signature, newMethod.signature);
            return false;
        }

        // 比较字节码指令
        if (oldMethod.instructions.size() != newMethod.instructions.size()) {
            log.debug("方法指令数量不同: old={}, new={}", 
                oldMethod.instructions.size(), newMethod.instructions.size());
            return false;
        }

        // 比较每条指令
        Iterator<?> oldIter = oldMethod.instructions.iterator();
        Iterator<?> newIter = newMethod.instructions.iterator();
        int index = 0;
        
        while (oldIter.hasNext() && newIter.hasNext()) {
            Object oldInsn = oldIter.next();
            Object newInsn = newIter.next();
            if (!Objects.equals(oldInsn, newInsn)) {
                log.debug("方法指令不同[{}]: old={}, new={}", 
                    index, oldInsn, newInsn);
                return false;
            }
            index++;
        }

        return true;
    }

    public Map<String, Set<List<String>>> analyzeMethodDiffs(String oldJarPath, String newJarPath, Set<String> allClasses) throws IOException {
        Map<String, Set<List<String>>> methodCallChains = new HashMap<>();
        
        for (String className : allClasses) {
            ClassNode oldClass = readClass(oldJarPath, className);
            ClassNode newClass = readClass(newJarPath, className);
            
            // 如果任一版本中找不到类，跳过
            if (oldClass == null || newClass == null) {
                continue;
            }
            
            // 比较方法的字节码
            Map<String, MethodNode> oldMethods = getMethodMap(oldClass.methods);
            Map<String, MethodNode> newMethods = getMethodMap(newClass.methods);
            
            Set<String> allMethodNames = new HashSet<>();
            allMethodNames.addAll(oldMethods.keySet());
            allMethodNames.addAll(newMethods.keySet());
            
            log.debug("分析类 {}, 方法总数: {}", className, allMethodNames.size());
            
            for (String methodName : allMethodNames) {
                MethodNode oldMethod = oldMethods.get(methodName);
                MethodNode newMethod = newMethods.get(methodName);
                
                // 检查方法是否有变化
                if (hasMethodChanged(oldMethod, newMethod)) {
                    String fullMethodName = className + "." + (oldMethod != null ? oldMethod.name : newMethod.name);
                    log.debug("发现变更方法: {}", fullMethodName);
                    if (!methodCallChains.containsKey(fullMethodName)) {
                        methodCallChains.put(fullMethodName, new HashSet<>());
                    }
                }
            }
        }
        
        log.info("方法差异分析完成，找到 {} 个变更方法", methodCallChains.size());
        return methodCallChains;
    }
    
    private boolean hasMethodChanged(MethodNode oldMethod, MethodNode newMethod) {
        // 如果方法在其中一个版本中不存在，则认为有变化
        if (oldMethod == null || newMethod == null) {
            log.debug("方法存在性变化: old={}, new={}", oldMethod != null, newMethod != null);
            return true;
        }
        
        // 比较方法签名
        if (!Objects.equals(oldMethod.desc, newMethod.desc) ||
            !Objects.equals(oldMethod.signature, newMethod.signature) ||
            !Objects.equals(oldMethod.access, newMethod.access)) {
            log.debug("方法签名变化: name={}, oldDesc={}, newDesc={}, oldSignature={}, newSignature={}, oldAccess={}, newAccess={}",
                    oldMethod.name, oldMethod.desc, newMethod.desc, oldMethod.signature, newMethod.signature, oldMethod.access, newMethod.access);
            return true;
        }
        
        // 比较方法体的字节码
        byte[] oldCode = oldMethod.instructions != null ? getMethodBytecode(oldMethod) : new byte[0];
        byte[] newCode = newMethod.instructions != null ? getMethodBytecode(newMethod) : new byte[0];
        
        boolean changed = !Arrays.equals(oldCode, newCode);
        if (changed) {
            log.debug("方法字节码变化: name={}, oldSize={}, newSize={}", 
                    oldMethod.name, oldCode.length, newCode.length);
        }
        return changed;
    }
    
    public byte[] getMethodBytecode(MethodNode methodNode) {
        if (methodNode.instructions == null) {
            return new byte[0];
        }
        
        // 计算方法指令的哈希值
        int hash = 0;
        for (AbstractInsnNode insn = methodNode.instructions.getFirst(); insn != null; insn = insn.getNext()) {
            hash = 31 * hash + insn.getOpcode();
            
            // 根据指令类型添加特定信息到哈希值
            if (insn instanceof MethodInsnNode) {
                MethodInsnNode methodInsn = (MethodInsnNode) insn;
                hash = 31 * hash + Objects.hashCode(methodInsn.owner + methodInsn.name + methodInsn.desc);
            } else if (insn instanceof FieldInsnNode) {
                FieldInsnNode fieldInsn = (FieldInsnNode) insn;
                hash = 31 * hash + Objects.hashCode(fieldInsn.owner + fieldInsn.name + fieldInsn.desc);
            } else if (insn instanceof LdcInsnNode) {
                hash = 31 * hash + Objects.hashCode(((LdcInsnNode) insn).cst);
            } else if (insn instanceof VarInsnNode) {
                hash = 31 * hash + ((VarInsnNode) insn).var;
            } else if (insn instanceof IntInsnNode) {
                hash = 31 * hash + ((IntInsnNode) insn).operand;
            } else if (insn instanceof TypeInsnNode) {
                hash = 31 * hash + Objects.hashCode(((TypeInsnNode) insn).desc);
            }
        }
        
        // 将哈希值转换为字节数组
        return new byte[] {
            (byte)(hash >>> 24),
            (byte)(hash >>> 16),
            (byte)(hash >>> 8),
            (byte)hash
        };
    }
}
